`timescale 1ns/1ps

module tb_main();

glbl glbl ();
// 时钟和复位信号
reg sys_clk;
reg sys_rst_n;

// 测试控制信号
reg start_transfer;
wire busy;
wire done;

// 测试数据
reg [31:0] test_data;

// SPI Master接口信号
wire [7:0] tx_byte;
wire tx_dv;
wire tx_ready;
wire rx_dv;
wire [7:0] rx_byte;

// SPI接口信号
wire spi_cs;
wire spi_sck;
wire spi_mosi;
reg spi_miso;

// 测试控制
integer test_case;
integer error_count;
integer success_count;

// 时钟生成
always #5 sys_clk = ~sys_clk; // 100MHz时钟

top top_inst (
    .input_clk(sys_clk),
    .rst_n(sys_rst_n)
);

// DUT实例
SPI_4Byte_Transmitter dut
(
    .i_Clk(sys_clk),
    .i_Rst_L(sys_rst_n),
    .i_Start(start_transfer),
    .o_Busy(busy),
    .o_Done(done),
    .i_Data_32bit(test_data),
    .o_TX_Byte(tx_byte),
    .o_TX_DV(tx_dv),
    .i_TX_Ready(tx_ready),
    .i_RX_DV(rx_dv),
    .i_RX_Byte(rx_byte),
    .o_SPI_CS(spi_cs)
);

// SPI Master模型实例
SPI_Master 
#(
    .SPI_MODE(3),
    .CLKS_PER_HALF_BIT(2)
)
spi_master_model
(
    .i_Rst_L(sys_rst_n),
    .i_Clk(sys_clk),
    .i_TX_Byte(tx_byte),
    .i_TX_DV(tx_dv),
    .o_TX_Ready(tx_ready),
    .o_RX_DV(rx_dv),
    .o_RX_Byte(rx_byte),
    .o_SPI_Clk(spi_sck),
    .i_SPI_MISO(spi_miso),
    .o_SPI_MOSI(spi_mosi)
);

// 测试任务：发送单个测试向量
task test_single_transfer;
    input [31:0] data;
    input [7:0] expected_byte0;
    input [7:0] expected_byte1;
    input [7:0] expected_byte2;
    input [7:0] expected_byte3;
    reg [7:0] received_bytes [0:3];
    integer byte_index;
    begin
        $display("=== 开始测试: 数据=0x%08h ===", data);
        test_data = data;
        start_transfer = 1'b1;
        @(posedge sys_clk);
        start_transfer = 1'b0;
        
        // 等待传输开始
        wait(busy == 1'b1);
        $display("传输开始，CS=%b", spi_cs);
        
        // 检查CS信号是否正确拉低
        if (spi_cs !== 1'b0) begin
            $display("错误: CS信号未正确拉低");
            error_count = error_count + 1;
        end
        
        // 收集发送的字节
        byte_index = 0;
        while (byte_index < 4) begin
            @(posedge sys_clk);
            if (tx_dv && tx_ready) begin
                received_bytes[byte_index] = tx_byte;
                $display("发送字节%d: 0x%02h", byte_index, tx_byte);
                byte_index = byte_index + 1;
            end
        end
        
        // 验证发送的字节
        if (received_bytes[0] !== expected_byte0) begin
            $display("错误: 字节0期望=0x%02h, 实际=0x%02h", expected_byte0, received_bytes[0]);
            error_count = error_count + 1;
        end
        if (received_bytes[1] !== expected_byte1) begin
            $display("错误: 字节1期望=0x%02h, 实际=0x%02h", expected_byte1, received_bytes[1]);
            error_count = error_count + 1;
        end
        if (received_bytes[2] !== expected_byte2) begin
            $display("错误: 字节2期望=0x%02h, 实际=0x%02h", expected_byte2, received_bytes[2]);
            error_count = error_count + 1;
        end
        if (received_bytes[3] !== expected_byte3) begin
            $display("错误: 字节3期望=0x%02h, 实际=0x%02h", expected_byte3, received_bytes[3]);
            error_count = error_count + 1;
        end
        
        // 等待传输完成
        wait(done == 1'b1);
        $display("传输完成，CS=%b", spi_cs);
        
        // 检查CS信号是否正确拉高
        if (spi_cs !== 1'b1) begin
            $display("错误: CS信号未正确拉高");
            error_count = error_count + 1;
        end
        
        // 检查busy信号是否正确
        if (busy !== 1'b0) begin
            $display("错误: busy信号未正确拉低");
            error_count = error_count + 1;
        end
        
        if (error_count == 0) begin
            success_count = success_count + 1;
            $display("测试通过!");
        end
        
        // 等待几个时钟周期再进行下一个测试
        repeat(10) @(posedge sys_clk);
    end
endtask

// 监控SPI信号
always @(posedge spi_sck) begin
    if (!spi_cs) begin // CS为低时有效
        $display("SPI时钟上升沿: MOSI=%b", spi_mosi);
    end
end

always @(negedge spi_sck) begin
    if (!spi_cs) begin // CS为低时有效
        $display("SPI时钟下降沿: MOSI=%b", spi_mosi);
    end
end

// 主测试流程
initial begin
    // 初始化
    sys_clk = 0;
    sys_rst_n = 0;
    start_transfer = 0;
    test_data = 32'h0;
    spi_miso = 0;
    error_count = 0;
    success_count = 0;
    test_case = 0;
    
    // 生成复位信号
    $display("应用复位...");
    #20 sys_rst_n = 1;
    $display("复位释放");
    
    // 等待系统稳定
    #100;
    
    $display("\n开始SPI 4字节传输测试...");
    
    // 测试用例1: 递增数据
    test_case = 1;
    test_single_transfer(32'h01020304, 8'h01, 8'h02, 8'h03, 8'h04);
    
    // 测试用例2: 递减数据
    test_case = 2;
    test_single_transfer(32'hF0E0D0C0, 8'hF0, 8'hE0, 8'hD0, 8'hC0);
    
    // 测试用例3: 全0数据
    test_case = 3;
    test_single_transfer(32'h00000000, 8'h00, 8'h00, 8'h00, 8'h00);
    
    // 测试用例4: 全1数据
    test_case = 4;
    test_single_transfer(32'hFFFFFFFF, 8'hFF, 8'hFF, 8'hFF, 8'hFF);
    
    // 测试用例5: 交替数据
    test_case = 5;
    test_single_transfer(32'hAA55AA55, 8'hAA, 8'h55, 8'hAA, 8'h55);
    
    // 测试用例6: 随机数据
    test_case = 6;
    test_single_transfer(32'h12345678, 8'h12, 8'h34, 8'h56, 8'h78);
    
    // 测试用例7: 连续传输测试
    $display("\n=== 连续传输测试 ===");
    test_case = 7;
    
    // 第一次传输
    test_data = 32'hDEADBEEF;
    start_transfer = 1'b1;
    @(posedge sys_clk);
    start_transfer = 1'b0;
    
    wait(done == 1'b1);
    $display("第一次传输完成");
    
    // 立即开始第二次传输
    #20;
    test_data = 32'hCAFEBABE;
    start_transfer = 1'b1;
    @(posedge sys_clk);
    start_transfer = 1'b0;
    
    wait(done == 1'b1);
    $display("第二次传输完成");
    
    // 总结测试结果
    $display("\n=== 测试总结 ===");
    $display("总测试用例: %0d", test_case);
    $display("成功用例: %0d", success_count);
    $display("错误数量: %0d", error_count);
    
    if (error_count == 0) begin
        $display("所有测试通过! ✓");
    end else begin
        $display("测试失败! ✗");
    end
    
    // 结束仿真
    #100;
    $finish;
end

// 超时保护
initial begin
    #100000; // 100us超时
    $display("错误: 仿真超时!");
    $finish;
end

// 生成VCD文件用于波形分析
initial begin
    $dumpfile("tb_main.vcd");
    $dumpvars(0, tb_main);
end

endmodule